#include "Primitive.h"
#include "Rect.h"
#include "Triangle.h"
#include "Cylinder.h"
#include "Sphere.h"
#include "Torus.h"
#include "Scene.h"
#include "ParsingErrorException.h"

map<string, ElementLoader*> Primitive::primitiveLoaders;

void Primitive::initLoaders() {
	if( primitiveLoaders.empty() ) {
		primitiveLoaders[Rect::Value()] = new ConcreteElementLoader<Rect>();
		primitiveLoaders[Triangle::Value()] = new ConcreteElementLoader<Triangle>();
		primitiveLoaders[Cylinder::Value()] = new ConcreteElementLoader<Cylinder>();
		primitiveLoaders[Sphere::Value()] = new ConcreteElementLoader<Sphere>();
		primitiveLoaders[Torus::Value()] = new ConcreteElementLoader<Torus>();
	}
}


void Primitive::parseMaterial(TiXmlElement *elem) {

	Scene::testTagValue(elem, Material::Value());

	string mat_id = Scene::testStringAttribute(elem, "id");

	if( mat_id == "inherit" )
		this->material = NULL;
		return;

	Scene::testIdAttribute(elem, &Scene::materials);
	this->material = Scene::materials[mat_id];
}

void Primitive::parseTexture(TiXmlElement *elem) {
	
	Scene::testTagValue(elem, Texture::Value());
	string tex_id = Scene::testStringAttribute(elem, "id");
	
	if( tex_id == "inherit" || tex_id == "none" ) {
		this->texture = NULL;
		return;
	}

	this->inherit_texture = false;
	Scene::testIdAttribute(elem, &Scene::textures);
	this->texture = Scene::textures[tex_id];
}

Primitive::Primitive(TiXmlElement *elem) {
	initLoaders();

	this->isPrimitive = true;

	string pri_id = Scene::testStringAttribute(elem, "id");

	if( Scene::primitives.count(pri_id) >= 1 )
		throw new ParsingErrorException("id already exists.");

	this->id = pri_id;

	// material tag
	TiXmlElement *materialPrimitive = elem->FirstChildElement();
	parseMaterial(materialPrimitive);

	// texture tag
	TiXmlElement *texturePrimitive = materialPrimitive->NextSiblingElement();
	parseTexture(texturePrimitive);

	// primitive object tag
	TiXmlElement *primitiveElement = texturePrimitive->NextSiblingElement();
	Scene::testTagValue(primitiveElement, primitiveLoaders);

	try {
		this->primitiveObject = (PrimitiveObject *) primitiveLoaders[primitiveElement->Value()]->createElement(primitiveElement);
	} catch(ParsingErrorException *e) {
		e->concatTag(primitiveElement);
		throw e;
	}
}

void Primitive::displayElement() { // TODO apply texture and material
	this->primitiveObject->displayElement();
}